﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Data;
using WyUnity.Utility.ExtentionMethods;
using WyUnity.Data.Query;
using WyUnity.Utility.Dynamic;
using WyUnity.Data.DataAccess;
using System.Reflection;
using WyUnity.Utility;

namespace WyUnity.Data.TableMapper
{
    /// <summary>
    /// 表映射基类
    /// </summary>
    public class TableBase<T> : ITable
        where T : TableBase<T>
    {
        #region 显示实现接口ITable

        Columns ITable.ToColumns()
        {
            var col = new Columns();
            foreach (var pg in _propsGetMethodCache)
            {
                if (!pg.Value.MapConfig.DBGenerate)
                {
                    col.Add(pg.Key, pg.Value.Function(this, new object[0]));
                }
            }
            return col;
        }

        WhereClip ITable.GetPrimaryKeyWhereClip()
        {
            WhereClip where = new WhereAll();
            foreach (var k in _keys)
            {
                where = where.And(k, _propsGetMethodCache[k].Function(this, new object[0]));
            }
            return where;
        }

        void IMapObject.LoadData(IDataReader dataReader)
        {
            foreach (var ps in _propsSetMethodCache)
            {
                var value = dataReader[ps.Key];
                if (value.IsNullOrDBNull()) continue;
                value = value.TryTo(ps.Value.PropertyInfo.PropertyType);
                ps.Value.Function(this, new object[] { value });
            }
        }

        string IMapObject.GetSQLClip(ISqlDialect dialect)
        {
            return string.Format(dialect.NameFormat, ((ITable)this).GetTableName());
        }
        #endregion

        /// <summary>
        /// 获取表名
        /// </summary>
        public static string GetSQLTableName()
        {
            var t = typeof(T);
            var atts = t.GetCustomAttributes(typeof(MapAttribute), false);
            if (atts.Length > 0)
            {
                var name = (atts[0] as MapAttribute).Name;
                if (!name.IsNullOrEmpty()) return name;
            }
            return t.Name;
        }

        #region 静态初始化
        /// <summary>
        /// 对象全部属性设置方法
        /// </summary>
        private static readonly Dictionary<string, PropertyMapping> _propsSetMethodCache;
        /// <summary>
        /// 对象全部属性读取方法
        /// </summary>
        private static readonly Dictionary<string, PropertyMapping> _propsGetMethodCache;
        /// <summary>
        /// 主键列表
        /// </summary>
        private static readonly List<string> _keys;

        private static readonly List<FieldDefine> _columns;
        ReadOnlyCollection<FieldDefine> ITable.DefineColumns
        {
            get { return _columns.AsReadOnly(); }
        }
        static TableBase()
        {
            _propsGetMethodCache = new Dictionary<string, PropertyMapping>();
            _propsSetMethodCache = new Dictionary<string, PropertyMapping>();
            _keys = new List<string>();
            _columns = new List<FieldDefine>();

            var type = typeof(T);
            var props = type.GetProperties();
            foreach (var p in props)
            {
                var atts = p.GetCustomAttributes(typeof(MapAttribute), true);
                if (atts.Length == 1)
                {
                    var mapattr = atts[0] as MapAttribute;

                    //以Map的FieldName为Key
                    var pname = mapattr.Name;
                    //如果没有设置FieldName，则已字段的名称为Key
                    if (pname.IsNullOrEmpty()) pname = p.Name;

                    if (mapattr.IsKeyField) _keys.Add(pname);

                    System.Diagnostics.Debug.Assert(!string.IsNullOrEmpty(pname));
                    //Log.Default.Debug(pname);

                    _columns.Add(new FieldDefine(pname, p.PropertyType, mapattr.IsKeyField) { Length = mapattr.Length, IsRowVersion = (mapattr is RowVersionAttribute) });

                    if (p.CanRead)
                    {
                        if (_propsGetMethodCache.ContainsKey(pname))
                            throw new DAException("字段名称重复");

                        _propsGetMethodCache.Add(pname,
                            new PropertyMapping
                            {
                                Function = DynamicCall.GetExecuteDelegate(p.GetGetMethod()),
                                MapConfig = mapattr,
                                PropertyInfo = p,
                            });
                    }
                    if (p.CanWrite)
                    {
                        if (_propsSetMethodCache.ContainsKey(pname))
                            throw new DAException("字段名称重复");

                        _propsSetMethodCache.Add(pname,
                            new PropertyMapping
                            {
                                Function = DynamicCall.GetExecuteDelegate(p.GetSetMethod()),
                                MapConfig = mapattr,
                                PropertyInfo = p,
                            });
                    }
                }
            }
        }
        #endregion

        struct PropertyMapping
        {
            public Func<object, object[], object> Function { get; set; }
            public PropertyInfo PropertyInfo { get; set; }
            public MapAttribute MapConfig { get; set; }
        }


        string ITable.GetTableName()
        {
            return GetSQLTableName();
        }
    }
}
